using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.Serialization;

namespace Shrapnel.Areas
{
	/// <summary>
	/// A line-shaped area, for lines with a certain width
	/// </summary>
	[DataContract()]
	public class Line : Part
	{
		/// <summary>
		/// Start of the line
		/// </summary>
		[DataMember(Name="Start")]
		private PointF start;

		/// <summary>
		/// End of the line
		/// </summary>
		[DataMember(Name="End")]
		private PointF end;

		/// <summary>
		/// Width of the line
		/// </summary>
		[DataMember(Name="Width")]
		private float width;


		/// <summary>
		/// Start of the line
		/// </summary>
		public System.Drawing.PointF Start
		{
			get { return start; }
			set { start = value; }
		}

		/// <summary>
		/// End of the line
		/// </summary>
		public System.Drawing.PointF End
		{
			get { return end; }
			set { end = value; }
		}

		/// <summary>
		/// Width of the line
		/// </summary>
		public float Width
		{
			get { return width; }
			set { width = value; }
		}

		/// <summary>
		/// Calculate a bounding box for this line
		/// </summary>
		/// <returns></returns>
		public override BoundingBox GetBoundingBox()
		{
			return new BoundingBox(
				new PointF(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y)),
				new SizeF(Math.Abs(start.X - end.X), Math.Abs(start.Y - end.Y )));
		}

		/// <summary>
		/// Move our line
		/// </summary>
		/// <param name="delta"></param>
		public override void Move(System.Drawing.PointF delta)
		{
			start.X += delta.X;
			start.Y += delta.Y;
			end.X += delta.Y;
			end.Y += delta.Y;
		}

		/// <summary>
		/// Exact overlap check against a given bounding box. 
		/// </summary>
		/// <param name="bbox">Some bounding box</param>
		/// <returns>True when the bounding box overlaps with this line</returns>
		public override bool OverlapsWith(BoundingBox bbox)
		{
			BoundingBox ownBbox = GetBoundingBox();

			if (ownBbox.OverlapsWith(bbox))			
			{
				if (width == 0.0f)
				{
					// Infinitely thin line?
					return bbox.OverlapsWith(start, end);
				} else 
				{
					PointF perpendicularDir = new PointF(start.Y - end.Y, end.X - start.X);
					float len = (float)Math.Sqrt(perpendicularDir.X * perpendicularDir.X + perpendicularDir.Y * perpendicularDir.Y);
					float halfWidth = width * 0.5f;	
					
					// Normalize and scale
					perpendicularDir.X *= halfWidth / len;
					perpendicularDir.Y *= halfWidth / len;
	
					// Create 4 corner points of our rotated rectangle that forms our line

					//      +
					//     /  \
					//    /     \
					//   /       +
					//  /       /
					// +       /
					//   \    /
					//     \ /
					//      +
					//

					PointF start1 = start;
					PointF end1 = end;
					PointF start2 = start;
					PointF end2 = end;

					start1.X += perpendicularDir.X;
					start1.Y += perpendicularDir.Y;
					end1.X += perpendicularDir.X;
					end1.Y += perpendicularDir.Y;

					start2.X -= perpendicularDir.X;
					start2.Y -= perpendicularDir.Y;
					end2.X -= perpendicularDir.X;
					end2.Y -= perpendicularDir.Y;

					// trace all 4 lines and our initial "center" line against our bbox
					return
						bbox.OverlapsWith(start,end) ||
						bbox.OverlapsWith(start1, end1) ||
						bbox.OverlapsWith(start2, end2) ||
						bbox.OverlapsWith(start1, start2) ||
						bbox.OverlapsWith(end1, end2);
				}
			}

			return false;
		}
	}
}
